#pragma once
#ifndef QUEUE_MT_H_
#define QUEUE_MT_H_

#include <cstdio>
#include <mutex>              // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <vector>

template<typename T, unsigned MAXLEN = 16>
class queue_mt
{
private:
    std::mutex mtx;
    std::condition_variable cond_get;
    std::condition_variable cond_put;
    unsigned pHead;
    unsigned pTail;
    T queue[MAXLEN];
public:
    queue_mt(): mtx(), cond_get(), cond_put(), pHead(0), pTail(0) {};
    ~queue_mt()
    {
        std::lock_guard<std::mutex> lck(mtx);
        if (pHead != pTail)
        {
            std::printf("[~queue_mt()]: queue is not empty, fix it!\n");
        }
    };
    T get()
    {
        std::unique_lock<std::mutex> lck(mtx);
        while (pHead == pTail)
        {
            cond_get.wait(lck);
        }
        T tmp = queue[pHead % MAXLEN];
        ++pHead;
        cond_put.notify_one();
        return tmp;
    };
    void put(const T& val)
    {
        std::unique_lock<std::mutex> lck(mtx);
        while (pHead + MAXLEN == pTail)
        {
            cond_put.wait(lck);
        }
        queue[pTail % MAXLEN] = val;
        ++pTail;
        cond_get.notify_one();
        return;
    };
};

#endif
